﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using A2DFramework.AOPService;
using System.Reflection;
using System.Reflection.Emit;
using A2DFramework.EsbService.Exceptions;
using A2DFramework.InstanceService;
using A2DFramework.QueueService;
using System.Xml.Linq;
using A2DFramework.EsbService.NativeEsb;
using A2DFramework.EsbService.DistributedEsb;

namespace A2DFramework.EsbService
{
    public class EsbFactory
    {
        static EsbFactory()
        {
            IQueue qSrv = ObjectFactory.Resolve<IQueue>();
            qSrv.CreateIfNotExists<EsbRequestWrapper>();
            qSrv.CreateIfNotExists<EsbResponseWrapper>();
        }

        public static T Create<T>()
            where T : IBaseContract
        {
            return Create<T>(string.Empty);
        }

        public static T Create<T>(string version)
            where T:IBaseContract
        {
            string id = string.Format("EsbProxy{0}", typeof(T).FullName.Replace(".", ""));

            AssemblyBuilder asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(id), AssemblyBuilderAccess.RunAndSave);
            var mdlBldr = asmBuilder.DefineDynamicModule(id, id+".dll");

            var typeBldr = mdlBldr.DefineType("EsbProxy"+id, TypeAttributes.Public, null, new Type[] { typeof(T) });

            //嵌入ESB私有变量
            var esbSrvField=typeBldr.DefineField("esbSrv", typeof(IEsb), FieldAttributes.Private);

            //构造函数
            var constructor=typeBldr.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] { typeof(IEsb) });
            var il=constructor.GetILGenerator();
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldarg_1);
            il.Emit(OpCodes.Stfld, esbSrvField);
            il.Emit(OpCodes.Ret);

            //改写方法
            var methods = typeof(T).GetMethods(BindingFlags.Public | BindingFlags.Instance);
            for (var i = 0; i < methods.Length; i++)
            {
                var paramTypes = GetParametersType(methods[i]);
                var methodBlfr = typeBldr.DefineMethod(methods[i].Name, MethodAttributes.Public | MethodAttributes.Virtual, CallingConventions.Standard, methods[i].ReturnType, paramTypes);
                
                il = methodBlfr.GetILGenerator();

                //声明各个局部变量
                var requestLocal=il.DeclareLocal(typeof(EsbRequestWrapper));
                var argsLocal = il.DeclareLocal(typeof(List<object>));
                var responseLocal=il.DeclareLocal(typeof(EsbResponseWrapper));
                var responseIsSuccessLocal = il.DeclareLocal(typeof(bool));
                var responseResult = il.DeclareLocal(typeof(object));
                var responseResultCasted = il.DeclareLocal(methods[i].ReturnType);
                var value2Object = il.DeclareLocal(typeof(object));

                il.EmitWriteLine("----111-----" + methods[i].Name);

                //初始化局部变量
                il.Emit(OpCodes.Newobj, typeof(EsbRequestWrapper).GetConstructor(new Type[0]));
                il.Emit(OpCodes.Stloc, requestLocal);

                il.Emit(OpCodes.Newobj, typeof(List<object>).GetConstructor(new Type[0]));
                il.Emit(OpCodes.Stloc, argsLocal);

                il.EmitWriteLine("----222-----" + methods[i].Name);

                //将传入方法的参数全部Add到List<object>中
                MethodInfo mi = typeof(List<object>).GetMethod("Add");
                if (paramTypes != null)
                {
                    for (var index = 0; index < paramTypes.Length; index++)
                    {
                        if (paramTypes[index].IsValueType)
                        {
                            //box
                            il.Emit(OpCodes.Ldarg, 1 + index);
                            il.Emit(OpCodes.Box, paramTypes[index]);
                            il.Emit(OpCodes.Stloc, value2Object);


                            il.Emit(OpCodes.Ldloc, argsLocal);
                            il.Emit(OpCodes.Ldloc, value2Object);
                            il.Emit(OpCodes.Callvirt, mi);
                        }
                        else
                        {
                            il.Emit(OpCodes.Ldloc, argsLocal);
                            il.Emit(OpCodes.Ldarg, 1 + index);
                            il.Emit(OpCodes.Callvirt, mi);
                        }
                    }
                }

                il.EmitWriteLine("----333-----" + methods[i].Name);

                mi = typeof(EsbRequestWrapper).GetMethod("set_Arguments");
                il.Emit(OpCodes.Ldloc, requestLocal);
                il.Emit(OpCodes.Ldloc, 1);
                il.Emit(OpCodes.Callvirt, mi);
                

                //将当前方法名称赋值给Method2Invoke
                mi = typeof(EsbRequestWrapper).GetMethod("set_Method2Invoke");
                il.Emit(OpCodes.Ldloc, requestLocal);
                il.Emit(OpCodes.Ldstr, methods[i].Name);
                il.Emit(OpCodes.Callvirt, mi);

                //设置AppID、CallContractNamespace、Version、CallContract
                mi = typeof(EsbRequestWrapper).GetMethod("set_AppID");
                il.Emit(OpCodes.Ldloc, requestLocal);
                il.Emit(OpCodes.Ldstr, "demo appId");
                il.Emit(OpCodes.Callvirt, mi);

                mi = typeof(EsbRequestWrapper).GetMethod("set_CallContractNamespace");
                il.Emit(OpCodes.Ldloc, requestLocal);
                il.Emit(OpCodes.Ldstr, typeof(T).Namespace);
                il.Emit(OpCodes.Callvirt, mi);

                mi = typeof(EsbRequestWrapper).GetMethod("set_Version");
                il.Emit(OpCodes.Ldloc, requestLocal);
                il.Emit(OpCodes.Ldstr, version);
                il.Emit(OpCodes.Callvirt, mi);

                mi = typeof(EsbRequestWrapper).GetMethod("set_CallContract");
                il.Emit(OpCodes.Ldloc, requestLocal);
                il.Emit(OpCodes.Ldstr, typeof(T).Name);
                il.Emit(OpCodes.Callvirt, mi);


                //调用Send方法，并且取得返回值
                mi = typeof(IEsb).GetMethod("Send");
                mi = mi.MakeGenericMethod(typeof(EsbRequestWrapper), typeof(EsbResponseWrapper));
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Ldfld, esbSrvField);
                il.Emit(OpCodes.Ldloc, requestLocal);
                il.Emit(OpCodes.Callvirt, mi);
                il.Emit(OpCodes.Stloc, responseLocal);

                //根据返回值判断是否已成功执行
                var lblConvertResult = il.DefineLabel();
                var lblContinue = il.DefineLabel();
                mi = typeof(EsbResponseWrapper).GetMethod("get_IsSuccessful");
                il.Emit(OpCodes.Ldloc, responseLocal);
                il.Emit(OpCodes.Callvirt, mi);
                il.Emit(OpCodes.Stloc, responseIsSuccessLocal);

                il.Emit(OpCodes.Ldloc, responseIsSuccessLocal);
                il.Emit(OpCodes.Ldc_I4_1);
                il.Emit(OpCodes.Ceq);
                il.Emit(OpCodes.Brtrue_S, lblConvertResult);
                //throw exception
                il.Emit(OpCodes.Newobj, typeof(RequestFailedException).GetConstructor(new Type[0]));
                il.Emit(OpCodes.Throw);
                il.Emit(OpCodes.Br_S, lblContinue);
                il.MarkLabel(lblConvertResult);


                mi = typeof(EsbResponseWrapper).GetMethod("get_Result");
                il.Emit(OpCodes.Ldloc, responseLocal);
                il.Emit(OpCodes.Callvirt, mi);
                il.Emit(OpCodes.Stloc, responseResult);
                il.Emit(OpCodes.Ldloc, responseResult);
                
                if(methods[i].ReturnType.IsValueType)
                    il.Emit(OpCodes.Unbox_Any, methods[i].ReturnType);
                else
                    il.Emit(OpCodes.Castclass, methods[i].ReturnType);
                il.Emit(OpCodes.Stloc, responseResultCasted);
                il.Emit(OpCodes.Ldloc, responseResultCasted);
                il.Emit(OpCodes.Br_S, lblContinue);
                il.MarkLabel(lblContinue);

                if (methods[i].ReturnType == typeof(void))
                    il.Emit(OpCodes.Pop);
                il.Emit(OpCodes.Ret);
            }

            var proxy=typeBldr.CreateType();

            asmBuilder.Save(id+".dll");

            return (T)Activator.CreateInstance(proxy, new object[] { ObjectFactory.Resolve<IEsb>() });
        }

        private static Type[] GetParametersType(MethodInfo method)
        {
            Type[] paramTypes = null;

            if (method != null)
            {
                var parameters = method.GetParameters();
                if (parameters.Length > 0)
                {

                    paramTypes = new Type[parameters.Length];

                    for (var i = 0; i < parameters.Length; i++)
                    {
                        paramTypes[i] = parameters[i].ParameterType;
                    }
                }
            }
            return paramTypes;
        }
    }
}
